﻿using System;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using Common;
using System.Collections.Generic;

namespace AuthorServiceLibrary
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    public class AuthorService : IAuthorService
    {
        private DataContext context = null;
        private const int ServiceID = 1;
        private NLog.Logger logger = null;

        public AuthorService()
        {
            context = new DataContext();
            logger = NLog.LogManager.GetCurrentClassLogger();
            Logger.type = "author";
        }

        public Response CreateArticle(string title, string text, string tags, string authorID, string attachmentID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                Article article = new Article()
                {
                    Title = title,
                    Text = text,
                    Tags = tags,
                    AuthorID = authorID,
                    AttachmentID = attachmentID,
                    Stage = MaturityStages.NewArticle,
                };

                lock (context)
                {
                    context.Articles.Add(article);
                    context.SaveChanges();
                }
                logger.Debug("Article successfully created. ID: " + article.ID);
                Logger.Write("Article successfully created. ID: " + article.ID); 
                response.Result.Add(article);
            }
            catch (Exception ex)
            {
                logger.ErrorException("Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method", ex);
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response UpdateArticle(string ID, string title, string text, string tags, string attachmentID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var article = (from a in context.Articles where a.ID == ID select a).SingleOrDefault();
                if (article != null)
                {
                    var checkedArticle = (from ca in context.CheckedArticles where ca.ArticleID == article.ID select ca).SingleOrDefault();
                    if (checkedArticle != null)
                    {
                        lock (context)
                        {
                            checkedArticle.IsVerified = false;
                            checkedArticle.Decision = ReviewDecision.NotTakenYet;
                            article.Title = title;
                            article.Text = text;
                            article.Tags = tags;
                            article.AttachmentID = attachmentID;
                            article.Stage = MaturityStages.UpdatedArticle;
                            context.SaveChanges();
                        }
                        response.Result.Add(article);
                    }
                }
                else
                {
                    logger.Warn("Article with ID '" + ID + "' not found in Database");
                    response.Error.Details = "Article with ID '" + ID + "' not found in Database";
                    response.Error.TargetSite = response.Action;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
            }
            catch (Exception ex)
            {
                logger.ErrorException("Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method", ex);
                //Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response RecieveArticles(string authorID, int pageIndex, int pageSize)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                lock (context)
                {

                    var articles = (from a in context.Articles where a.AuthorID == authorID select a).ToList();
                    if (articles.Count() == 0)
                    {
                        logger.Warn("No articles found for author id '" + authorID + "'");
                        response.Error.Details = "No articles found for author id '" + authorID + "'";
                        response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                        response.Error.Type = ErrorTypes.ObjectNotFound;
                    }
                    else
                    {
                        var sorted = from a in articles orderby a.CreationTime descending select a;
                        response.TotalResultsCount = articles.Count();
                        if (pageIndex != -1 && pageSize != -1)
                        {
                            response.Result.AddRange(sorted.Skip(pageIndex * pageSize).Take(pageSize));
                        }
                        else
                        {
                            response.Result.AddRange(articles);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                logger.ErrorException("Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method", ex);
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetArticleDetails(string articleID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var article = (from a in context.Articles where a.ID == articleID select a).SingleOrDefault();
                if (article == null)
                {
                    logger.Warn("Article with id '" + articleID + "' not found");
                    response.Error.Details = "Article with id '" + articleID + "' not found";
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    response.Result.Add(article);
                }
            }
            catch (Exception ex)
            {
                logger.ErrorException("Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method", ex);
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetReviews(string authorID, int pageIndex, int pageSize)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                int stage = (int)ReviewStatus.Approved;
                var reviews = (from a in context.Articles from r in context.Reviews 
                               where a.AuthorID == authorID && r.ArticleID == a.ID && r.stage == stage 
                               select r).ToList();
                if (reviews.Count == 0)
                {
                    response.Error.Details = "Reviews for author '" + authorID + "' not found";
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    var sorted = from r in reviews orderby r.CreationTime descending select r;
                    response.TotalResultsCount = sorted.Count();
                    if (pageIndex != -1 && pageSize != -1)
                    {
                        response.Result.AddRange(sorted.Skip(pageIndex * pageSize).Take(pageSize));
                    }
                    else
                    {
                        response.Result.AddRange(reviews);
                    }
                }
            }
            catch (Exception ex)
            {
                logger.ErrorException("Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method", ex);
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetArticleReviews(string articleID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                int stage = (int)ReviewStatus.Approved;
                var reviews = (from r in context.Reviews
                               where r.ArticleID == articleID && r.stage == stage
                               select r).ToList();
                if (reviews.Count == 0)
                {
                    response.Error.Details = "Reviews for article '" + articleID + "' not found";
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    var sorted = reviews.OrderByDescending(r => r.CreationTime).ToList();
                    response.Result.AddRange(sorted);
                }
            }
            catch (Exception ex)
            {
                logger.ErrorException("Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method", ex);
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetLastArticleReview(string articleID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var approved = (int)ReviewStatus.Approved;
                var reviews = (from r in context.Reviews where r.ArticleID == articleID && r.stage == approved select r).ToList();
                var lastReview = reviews.OrderByDescending(r => r.CreationTime).FirstOrDefault();

                if (lastReview != null)
                {
                    response.Result.Add(lastReview);
                }
                else
                {
                    response.Error.Details = "Reviews for article '" + articleID + "' not found";
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
            }
            catch (Exception ex)
            {
                logger.ErrorException("Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method", ex);
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetLastArticleReviews(string authorID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var approved = (int)ReviewStatus.Approved;
                var articles = (from a in context.Articles where a.AuthorID == authorID select a.ID).ToList();
                var reviews = (from a in articles
                               from r in context.Reviews
                               where r.ArticleID == a && r.stage == approved
                               select r).ToList().OrderByDescending(r => r.CreationTime);

                foreach (var article in articles)
                {
                    var exist = reviews.FirstOrDefault(r => r.ArticleID == article);
                    if (exist != null)
                        response.Result.Add(exist);
                }

                //else
                //{
                //    response.Error.Details = "Reviews for article '" + articleID + "' not found";
                //    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                //    response.Error.Type = ErrorTypes.ObjectNotFound;
                //}
            }
            catch (Exception ex)
            {
                logger.ErrorException("Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method", ex);
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }
    }
}